package com.bytebuddytest.fieldandmethod;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import org.junit.Assert;
import org.junit.Test;

import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.not;

/**
 * @author yutianhong
 * @version 1.0
 * @since 2023/10/24 20:48
 */
@SuppressWarnings({"resource", "unused"})
public class FieldAccessorTest {
    public static class UserType {
        public String doSomething() {
            return null;
        }
    }

    public interface Interceptor {
        String doSomethingElse();
    }

    public interface InterceptionAccessor {
        Interceptor getInterceptor();

        void setInterceptor(Interceptor interceptor);
    }

    public interface InstanceCreator {
        Object makeInstance();
    }

    @Test
    public void testFieldAccessor() throws Exception {
        Class<? extends UserType> dynamicUserType = new ByteBuddy()
                .subclass(UserType.class)
                .method(not(isDeclaredBy(Object.class)))
                .intercept(MethodDelegation.toField("interceptor"))
                .defineField("interceptor", Interceptor.class, Visibility.PRIVATE)
                .implement(InterceptionAccessor.class)
                .intercept(FieldAccessor.ofBeanProperty())
                .make()
                .load(getClass().getClassLoader())
                .getLoaded();

        InstanceCreator factory = new ByteBuddy()
                .subclass(InstanceCreator.class)
                .method(not(isDeclaredBy(Object.class)))
                .intercept(MethodDelegation.toConstructor(dynamicUserType))
                .make()
                .load(dynamicUserType.getClassLoader())
                .getLoaded()
                .newInstance();

        class HelloWorldInterceptor implements Interceptor {
            @Override
            public String doSomethingElse() {
                return "Hello World!";
            }
        }

        UserType userType = (UserType) factory.makeInstance();
        ((InterceptionAccessor) userType).setInterceptor(new HelloWorldInterceptor());

        Assert.assertEquals("Hello World!", userType.doSomething());
    }

}
